home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockYoutubeService.js < prev    next >
Text File  |  2007-10-12  |  47KB  |  1,513 lines

  1. // vim: ts=2 sw=2 expandtab cindent
  2. //
  3. // BEGIN FLOCK GPL
  4. // 
  5. // Copyright Flock Inc. 2005-2007
  6. // http://flock.com
  7. // 
  8. // This file may be used under the terms of of the
  9. // GNU General Public License Version 2 or later (the "GPL"),
  10. // http://www.gnu.org/licenses/gpl.html
  11. // 
  12. // Software distributed under the License is distributed on an "AS IS" basis,
  13. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. // for the specific language governing rights and limitations under the
  15. // License.
  16. // 
  17. // END FLOCK GPL
  18. //
  19.  
  20. // Developer ID = 1aiV9CsTs3o
  21. // Developer Secret = flock_is_open_source
  22.  
  23. const ENABLE_DEBUG = true; // switch to turn off slow debug code for production
  24. function DEBUG(x) { if (ENABLE_DEBUG) debug("flockYouTubeService: "+x+"\n"); }
  25.  
  26. const Cc = Components.classes;
  27. const Ci = Components.interfaces;
  28. const Cr = Components.results;
  29. const Cu = Components.utils;
  30.  
  31. const YT_DEVID = "1aiV9CsTs3o";
  32. const YT_DEVSECRET = "flock_is_open_source";
  33. const YOUTUBE_CID = Components.ID('{DCB6A01E-7D4A-4C30-AB3D-9CC98C02F617}');
  34. const YOUTUBE_CONTRACTID = '@flock.com/?photo-api-youtube;1';
  35. const YOUTUBE_FAVICON = "http://www.youtube.com/favicon.ico";
  36. const YOUTUBE_TITLE = "YouTube Web Service";
  37. const SERVICE_ENABLED_PREF          = "flock.service.youtube.enabled";
  38. const CATEGORY_COMPONENT_NAME       = "YouTube JS Component"
  39. const CATEGORY_ENTRY_NAME           = "youtube"
  40.  
  41. const FLOCK_PHOTO_CONTRACTID        = '@flock.com/photo;1';
  42. const FLOCK_PHOTOPERSON_CONTRACTID  = '@flock.com/photo-person;1';
  43. const FLOCK_PHOTO_ALBUM_CONTRACTID  = '@flock.com/photo-album;1';
  44. const PHOTOAPIMGR_CONTRACTID        = "@flock.com/photo-api-manager;1?";
  45.  
  46. const OBS_TOPIC_XPCOMSHUTDOWN = "xpcom-shutdown";
  47.  
  48. var gCompTK;
  49. function getCompTK() {
  50.   if (!gCompTK) {
  51.     gCompTK = Cc["@flock.com/singleton;1"]
  52.                 .getService(Ci.flockISingleton)
  53.                 .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  54.                 .wrappedJSObject;
  55.   }
  56.   return gCompTK;
  57. }
  58.  
  59. loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
  60.  
  61. var gTimers = [];  // For use with the scheduler
  62.  
  63. // String defaults... may be updated later through Web Detective
  64. var gStrings = {
  65.   "domains": "youtube.com",
  66.   "userlogin": "http://www.youtube.com/",
  67.   "userprofile": "http://www.youtube.com/profile?user=%accountid%",
  68.   "editprofile": "http://youtube.com/my_profile",
  69.   "inbox": "http://youtube.com/my_messages",
  70.   "subscriptions": "http://youtube.com/subscription_center"
  71. };
  72.  
  73. /* JMC - Notes for refactoring
  74.  
  75. CTOR - Always addsObserver for flock-data-ready,
  76. so that the service can be initialized with a proper faves-coop.
  77.  
  78. Updating actions at CTOR time is expensive
  79. - do in polling instead?
  80. - or on state change
  81.  
  82. */
  83.  
  84.  
  85. function youtubeVideo() {
  86. }
  87.  
  88. youtubeVideo.prototype= {
  89.   id: "",
  90.   thumbnail: "",
  91.   webPageUrl: "",
  92.   midSizePhoto: "",
  93.   largeSizePhoto: "",
  94.   title: "",
  95.   username: "",
  96.   userid: "",
  97.   is_public: "true",
  98.   is_video: "true",
  99.   has_miniView: "true",
  100.   svcShortName: 'youtube',
  101.   buildTooltip: function( ) {
  102.     // do we have to use document from the window to ceate elements? -- ja
  103.     var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
  104.                 .getService(Ci.nsIWindowMediator);
  105.     var win = wm.getMostRecentWindow('navigator:browser');
  106.     if (!win) return null;
  107.  
  108.     var box = win.document.createElement('vbox');
  109.     box.setAttribute('style', 'max-width: 250px');
  110.  
  111.     var title = win.document.createElement('label');
  112.     title.setAttribute('value', this.title );
  113.     title.setAttribute('crop', 'end');
  114.     box.appendChild(title);
  115.  
  116.     if( this.length_seconds)
  117.     {
  118.       var lbl = win.document.createElement('label');
  119.       lbl.setAttribute('value', 'Length: ' + this.length_seconds + ' seconds');
  120.       box.appendChild(lbl);
  121.     }
  122.     if(this.rating_avg)
  123.     {
  124.       var lbl = win.document.createElement('label');
  125.       lbl.setAttribute('value', 'Rating: ' + this.rating_avg + ' (' + this.rating_count + ' times)');
  126.       box.appendChild(lbl);
  127.     }
  128.     if(this.view_count)
  129.     {
  130.       var lbl = win.document.createElement('label');
  131.       lbl.setAttribute('value', 'Views: ' + this.view_count );
  132.       box.appendChild(lbl);
  133.     }
  134.  
  135.     if(!(this.length_seconds && this.rating_avg && this.view_count))
  136.     {
  137.       //if the tooltip does not these metadata -- show the author
  138.       var lbl = win.document.createElement('label');
  139.       lbl.setAttribute('value', this.username );
  140.       lbl.setAttribute('class', 'user');
  141.       box.appendChild(lbl);
  142.     }
  143.  
  144.     var vbox = win.document.createElement('vbox');
  145.     var cbox = win.document.createElement('cbox');
  146.     var largeImg = win.document.createElement('image');
  147.     largeImg.setAttribute('src', this.midSizePhoto);
  148.     largeImg.setAttribute('style', 'margin-bottom: 2px;');
  149.     var spacer = win.document.createElement('spacer');
  150.     spacer.setAttribute('flex', '1');
  151.     cbox.appendChild(largeImg);
  152.     cbox.appendChild(spacer);
  153.     vbox.appendChild(cbox);
  154.     vbox.appendChild(box);
  155.  
  156.     return vbox;
  157.   },
  158.   buildHTML: function ( ) {
  159.     var flashUrl = this.webPageUrl.replace(/\/\?v\=/,'/v/');
  160.  
  161.     return '<object width="425" height="350">'
  162.          + '<param name="movie" value="'+flashUrl+'"/>'
  163.          + '<embed src="'+flashUrl+'" type="application/x-shockwave-flash" width="425" height="350"/></object>';
  164.   },
  165.   buildBBCode: function ( ) {
  166.     var video = this.webPageUrl.replace(/\/\?v\=/,'/watch?v=');
  167.     return '[youtube]' + video + '[/youtube]'
  168.   },
  169.   buildMiniPage: function ( ) {
  170.     var theurl = this.webPageUrl.replace(/\/\?v\=/,'/v/');
  171.     return '<html><head><title>' + this.title + ' (' + this.username + ')</title></head>' +
  172.            '<body><object width="425" height="350">' +
  173.            '<param name="movie" value="'+theurl+'"/>' +
  174.            '<center><embed src="'+theurl+'&autoplay=1" type="application/x-shockwave-flash" width="425" height="350"/></object></center></body></html>';
  175.   },
  176.  
  177.   QueryInterface: function(iid) {
  178.     if (!iid.equals(Ci.nsISupports) &&
  179.         !iid.equals(Ci.flockIPhoto)) {
  180.       throw Components.results.NS_ERROR_NO_INTERFACE;
  181.     }
  182.     return this;
  183.   }
  184. };
  185.  
  186. youtubeVideo.prototype.__defineGetter__('metaData', function () {
  187.   var metaData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag2);
  188.   metaData.setPropertyAsAString("title", this.title);
  189.   metaData.setPropertyAsAString("length", this.length_seconds);
  190.   metaData.setPropertyAsAString("views", this.view_count);
  191.   metaData.setPropertyAsAString("rating", this.rating_avg);
  192.  
  193.   metaData.QueryInterface(Ci.nsIPropertyBag);
  194.  
  195.   return metaData;
  196. })
  197.  
  198.  
  199. // ================================================
  200. // ========== BEGIN youtubeService class ==========
  201. // ================================================
  202.  
  203. function youtubeService()
  204. {
  205.   this.obs = Components.classes["@mozilla.org/observer-service;1"]
  206.                        .getService(Components.interfaces.nsIObserverService);
  207.   this.obs.addObserver(this, OBS_TOPIC_XPCOMSHUTDOWN, false);
  208.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  209.                            .getService(Components.interfaces.flockIAccountUtils);
  210.   this.status = Components.interfaces.flockIWebService.STATUS_UNKNOWN;
  211.   this.url = "http://www.youtube.com";
  212.   this.mIsInitialized = false;
  213.   this._ctk = {
  214.     interfaces: [
  215.       "nsISupports",
  216.       "nsISupportsCString",
  217.       "nsIClassInfo",
  218.       "nsIObserver",
  219.       "flockIWebService",
  220.       "flockIMediaWebService",
  221.       "flockISocialWebService",
  222.       "flockIManageableWebService",
  223.       "flockIPhotoAPI",
  224.       "flockIPollingService",
  225.       //"flockIPeopleActionController",
  226.     ],
  227.     shortName: "youtube",
  228.     fullName: "YouTube",
  229.     description: YOUTUBE_TITLE,
  230.     favicon: YOUTUBE_FAVICON,
  231.     CID: YOUTUBE_CID,
  232.     contractID: YOUTUBE_CONTRACTID,
  233.     accountClass: youtubeAccount,
  234.     needPassword: false
  235.   };
  236.   this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  237.   this._logger.init('youtube');
  238.   this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
  239.   this.init();
  240. }
  241.  
  242.  
  243. // BEGIN nsIObserver interface
  244. youtubeService.prototype.observe =
  245. function youtubeService_observe(subject, topic, state)
  246. {
  247.   switch (topic) {
  248.     case OBS_TOPIC_XPCOMSHUTDOWN:
  249.     {
  250.       this.obs.removeObserver(this, OBS_TOPIC_XPCOMSHUTDOWN);
  251.     }; break;
  252.   }
  253. }
  254. // END nsIObserver interface
  255.  
  256.  
  257. youtubeService.prototype.init =
  258. function youtubeService_init()
  259. {
  260.   DEBUG(".init()");
  261.  
  262.   // Prevent re-entry
  263.   if (this.mIsInitialized) return;
  264.   this.mIsInitialized = true;
  265.  
  266.   var evtID = this._profiler.profileEventStart("youtube-init");
  267.  
  268.   this.prefService = Components.classes["@mozilla.org/preferences-service;1"]
  269.                                .getService(Components.interfaces.nsIPrefBranch);
  270.   if ( this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
  271.        !this.prefService.getBoolPref(SERVICE_ENABLED_PREF) )
  272.   {
  273.     DEBUG("Pref "+SERVICE_ENABLED_PREF+" set to FALSE... not initializing.");
  274.     var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  275.     catMgr.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true);
  276.     catMgr.deleteCategoryEntry("flockIPhotoAPI", CATEGORY_ENTRY_NAME, true);
  277.     catMgr.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true);
  278.     return;
  279.   }
  280.  
  281.   this.faves_coop = Components.classes['@flock.com/singleton;1']
  282.                               .getService(Components.interfaces.flockISingleton)
  283.                               .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  284.                               .wrappedJSObject;
  285.   this.account_root = this.faves_coop.accounts_root;
  286.   this.ytPersonRoot = new this.faves_coop.Folder("urn:youtube:peopleroot", {name: "YouTube People"});
  287.  
  288.   this.ytService = new this.faves_coop.Service(
  289.     "urn:youtube:service",
  290.     {
  291.       name: "youtube",
  292.       desc: "The YouTube Service",
  293.       loginURL: gStrings["userlogin"],
  294.       contactLabel: 'Contacts'
  295.     }
  296.   );
  297.   this.ytService.serviceId = YOUTUBE_CONTRACTID;
  298.   var ytHomepage = new this.faves_coop.Favorite(
  299.   "urn:flock:youtube:actions:homepage", {
  300.     name: "youtube.com",
  301.     URL: "http://www.youtube.com"
  302.   });
  303.  
  304.   var ytOpenProfile = new this.faves_coop.Action(
  305.   "urn:flock:youtube:actions:openprofile", {
  306.     name: "Open Profile",
  307.     method: "openProfile",
  308.     service: YOUTUBE_CONTRACTID,
  309.     flavour: "view"
  310.   });
  311.  
  312.   var ytMySubs = new this.faves_coop.Action(
  313.   "urn:flock:youtube:actions:mysubscriptions", {
  314.     name: "Open Subscriptions",
  315.     method: "openSubscriptions",
  316.     service: YOUTUBE_CONTRACTID,
  317.     flavour: "accountview"
  318.   });
  319.  
  320.   var ytMyInbox = new this.faves_coop.Action(
  321.   "urn:flock:youtube:actions:myinbox", {
  322.     name: "Open Inbox",
  323.     method: "openInbox",
  324.     service: YOUTUBE_CONTRACTID,
  325.     flavour: "accountview"
  326.   });
  327.  
  328.   var ytMyVideos = new this.faves_coop.Action(
  329.   "urn:flock:youtube:actions:myvideos", {
  330.     name: "Open Videos",
  331.     method: "openVideos",
  332.     service: YOUTUBE_CONTRACTID,
  333.     flavour: "view"
  334.   });
  335.  
  336.   var ytMyFavoriteVideos = new this.faves_coop.Action(
  337.    "urn:flock:youtube:actions:myfavvideos", {
  338.     name: "Open Favorite Videos",
  339.     method: "openFavVideos",
  340.     service: YOUTUBE_CONTRACTID,
  341.     flavour: "view",
  342.     isPrimary: true
  343.   });
  344.  
  345.   var ytPostComment = new this.faves_coop.Action(
  346.   "urn:flock:youtube:actions:postcomment", {
  347.     name: "Post Comment",
  348.     method: "postComment",
  349.     service: YOUTUBE_CONTRACTID,
  350.     flavour: "contact"
  351.   });
  352.  
  353.   var ytEditProfile = new this.faves_coop.Action(
  354.   "urn:flock:youtube:actions:editprofile", {
  355.     name: "Edit Profile",
  356.     method: "editProfile",
  357.     service: YOUTUBE_CONTRACTID,
  358.     flavour: "accountaction"
  359.   });
  360.  
  361.   var ytViewFriends = new this.faves_coop.Action(
  362.   "urn:flock:youtube:actions:viewFriends", {
  363.      name: "View Contacts",
  364.      method: "viewFriends",
  365.      service: YOUTUBE_CONTRACTID,
  366.     flavour: 'view'
  367.   });
  368.  
  369.   this.ytService.children.addOnce(ytViewFriends);
  370.   this.ytService.children.addOnce(ytHomepage);
  371.   this.ytService.children.addOnce(ytOpenProfile);
  372.   this.ytService.children.addOnce(ytMySubs);
  373.   this.ytService.children.addOnce(ytMyInbox);
  374.   this.ytService.children.addOnce(ytMyVideos);
  375.   this.ytService.children.addOnce(ytMyFavoriteVideos);
  376.   this.ytService.children.addOnce(ytPostComment);
  377.   this.ytService.children.addOnce(ytEditProfile);
  378.  
  379.   // Load Web Detective file
  380.   this.webDetective = this.acUtils.useWebDetective("youtube.xml");
  381.   for (var s in gStrings) {
  382.     gStrings[s] = this.webDetective.getString("youtube", s, gStrings[s]);
  383.   }
  384.   this.ytService.domains = gStrings["domains"];
  385.  
  386.   this.urn = this.ytService.id();
  387.  
  388.   var accounts = this.faves_coop.Account.find({serviceId: YOUTUBE_CONTRACTID});
  389.   if (accounts.length) {
  390.     this.USER = accounts[0].accountId;
  391.     for (var i = 0; i < accounts.length; i++) {
  392.       this.updateActions(accounts[i].id());
  393.     }
  394.   }
  395.  
  396.   this._profiler.profileEventEnd(evtID, "");
  397. }
  398.  
  399. youtubeService.prototype.getError =
  400. function youtubeService_getError (aErrorType, aXML, aHTTPErrorCode) {
  401.   var error = Components.classes["@flock.com/error;1"].createInstance(Ci.flockIError);
  402.   if  (aErrorType == "HTTP_ERROR") {
  403.     error.errorCode = aHTTPErrorCode;
  404.   } else if (aErrorType == "SERVICE_ERROR") {
  405.     var errorCode;
  406.     var errorMessage;
  407.     var serviceErrorMessage;
  408.     try {
  409.       errorCode = aXML.getElementsByTagName("error")[0].getAttribute('code');
  410.       serviceErrorMessage = aXML.getElementsByTagName("error")[0].getAttribute('description');
  411.     } catch (ex) {
  412.       errorCode = "999" // in case the error xml is invalid
  413.     }
  414.  
  415.     switch (errorCode) { // http://www.youtube.com/dev_error_codes
  416.       case "1":
  417.         error.errorCode = error.HTTP_INTERNAL_SERVER_ERROR;
  418.       break;
  419.  
  420.       case "2": // These errors are due to Flock sending a bad request
  421.       case "3":
  422.       case "4":
  423.       case "5":
  424.       case "6":
  425.         error.errorCode = error.PHOTOSERVICE_INVALID_QUERY;
  426.       break;
  427.  
  428.       case "7":
  429.       case "8":
  430.         error.errorCode = error.PHOTOSERVICE_INVALID_API_KEY;
  431.         break;
  432.  
  433.       case "999":
  434.         error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
  435.       break;
  436.  
  437.       default:
  438.         error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
  439.       break;
  440.     }
  441.   }
  442.   error.serviceErrorCode = errorCode;
  443.   error.serviceErrorString = serviceErrorMessage;
  444.   this._logger.error(error.errorString);
  445.   return error;
  446. };
  447.  
  448. // BEGIN flockIPhotoAPI interface
  449. youtubeService.prototype.__defineGetter__('authState', function () { return this.state; })
  450. youtubeService.prototype.__defineGetter__('firstRunPerson', function () { throw "NotImplemented"; })
  451. youtubeService.prototype.iconUrl = YOUTUBE_FAVICON;
  452. youtubeService.prototype.__defineGetter__('isLoggedIn', function () { return this.api.isLoggedIn; })
  453. youtubeService.prototype.__defineGetter__('isUploading', function ()  { return this.running; })
  454. youtubeService.prototype.serviceName = "YouTube";
  455. youtubeService.prototype.shortName = "youtube";
  456.  
  457.  
  458. youtubeService.prototype.authenticatedCall = function(aListener, aMethod, aParams) {
  459.   throw "NotImplemented";
  460. }
  461.  
  462. var channels = {
  463.   'special:added': {
  464.     title: 'Recently Added',
  465.     supportsSearch: false,
  466.     feed: 'http://youtube.com/rss/global/recently_added.rss'
  467.   },
  468.   'special:featured': {
  469.     title: 'Recently Featured',
  470.     supportsSearch: false
  471.   },
  472.   'special:top_favorites': {
  473.     title: 'Top Favorites',
  474.     supportsSearch: false,
  475.     feed: 'http://youtube.com/rss/global/top_favorites.rss'
  476.   },
  477.   'special:rated': {
  478.     title: 'Top Rated',
  479.     supportsSearch: false,
  480.     feed: 'http://youtube.com/rss/global/top_rated.rss'
  481.   }
  482. }
  483.  
  484. youtubeService.prototype.supportsSearch =
  485. function youtubeService_supportsSearch( aQueryString ) {
  486.   var aQuery = new queryHelper(aQueryString);
  487.  
  488.   if (aQuery.special) {
  489.     var channel = channels["special:" + aQuery.special];
  490.     if (channel) {
  491.       return channel.supportsSearch;
  492.     }
  493.   }
  494.  
  495.   // none of the other apis/feeds support search
  496.   return false;
  497. }
  498.  
  499. youtubeService.prototype.getChannel =
  500. function youtubeService_getChannel(aChannelId)
  501. {
  502.   if (!(aChannelId in channels)) return null;
  503.  
  504.   var nc = Components.classes['@flock.com/media-channel;1']
  505.                      .createInstance(Components.interfaces.flockIMediaChannel);
  506.   nc.id = aChannelId;
  507.   nc.title = channels[aChannelId].title
  508.   nc.supportsSearch = channels[aChannelId].supportsSearch;
  509.   return nc;
  510. }
  511.  
  512. youtubeService.prototype.__defineGetter__('channels', function () {
  513.   var ar = [];
  514.  
  515.   for (var id in channels) {
  516.     var nc = Components.classes['@flock.com/media-channel;1']
  517.                        .createInstance(Components.interfaces.flockIMediaChannel);
  518.     nc.id = id;
  519.     nc.title = channels[id].title
  520.     nc.supportsSearch = channels[id].supportsSearch;
  521.     ar.push(nc);
  522.   }
  523.  
  524.   var rval = {
  525.     getNext: function() {
  526.       var rval = ar.shift();
  527.       return rval;
  528.     },
  529.     hasMoreElements: function() {
  530.       return (ar.length > 0);
  531.     }
  532.   }
  533.   return rval;
  534. })
  535.  
  536.  
  537. youtubeService.prototype.call =
  538. function youtubeService_call(aListener, aMethod, aParams)
  539. {
  540.   var url = "http://www.youtube.com/api2_rest?method=" + aMethod + "&dev_id=" + YT_DEVID;
  541.   for (p in aParams) {
  542.     url += "&" + p + "=" + aParams[p];
  543.   }
  544.   var hr = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
  545.                      .createInstance(Components.interfaces.nsIXMLHttpRequest)
  546.                      .QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  547.  
  548.   var inst = this;
  549.   hr.onreadystatechange = function (aEvt) {
  550.     if (hr.readyState == 4) {
  551.       try {
  552.         if (hr.status/100 == 2) {
  553.           var rsp = hr.responseXML.getElementsByTagName("ut_response")[0];
  554.           var stat = rsp.getAttribute("status");
  555.           if (stat != "ok") {
  556.             var error = inst.getError('SERVICE_ERROR', hr.responseXML, null);
  557.             aListener.onError(error);
  558.           }
  559.           else {
  560.             aListener.onResult(hr.responseXML);
  561.           }
  562.         }
  563.         else {
  564.           // http errors
  565.           aListener.onError(inst.getError("HTTP_ERROR", null, hr.status));
  566.         }
  567.       } catch(e) {
  568.         // XMHTTPERROR (connection lost)
  569.         inst._logger.error(e);
  570.         aListener.onError(inst.getError("HTTP_ERROR", null, "9001"));
  571.       }
  572.     }
  573.   };
  574.  
  575.   hr.detachLoadGroup = true;
  576.   hr.open('GET',  url,true);
  577.   hr.send(null);
  578. }
  579.  
  580. youtubeService.prototype.getFeed =
  581. function youtubeService_getFeed(aListener, aFeedURL)
  582. {
  583.   ios = Components.classes["@mozilla.org/network/io-service;1"]
  584.     .getService(Components.interfaces.nsIIOService);
  585.   var uri = ios.newURI(aFeedURL, null, null);
  586.  
  587.   var ytServ = this;
  588.   var feedListener = {
  589.     onGetFeedComplete: function oncomplete (feed) {
  590.       ytServ.handleFeedResult(aListener, feed);
  591.     },
  592.     onError: aListener.onError
  593.   };
  594.  
  595.   var fm = Components.classes["@flock.com/feed-manager;1"]
  596.     .getService(Components.interfaces.flockIFeedManager);
  597.   fm.getFeed(uri, feedListener);
  598. }
  599.  
  600. youtubeService.prototype.createAlbum =
  601. function youtubeService_createAlbum(aListener, aAlbumName)
  602. {
  603.   throw "NotImplemented";
  604. }
  605.  
  606. youtubeService.prototype.findByUsername =
  607. function youtubeService_findByUsername(aListener, aUsername)
  608. {
  609.   var inst = this;
  610.   var myListener = {
  611.     onResult: function (aXML) {
  612.       var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID]
  613.                                  .createInstance(Components.interfaces.flockIPhotoPerson);
  614.       newUserObj.service = inst;
  615.       newUserObj.id = aUsername;
  616.       newUserObj.username = aUsername;
  617.       newUserObj.fullname = aUsername;
  618.       aListener.onFindByUsernameResult(newUserObj);
  619.     },
  620.     onError: function (aXML) {
  621.       aListener.onError(aXML);
  622.     }
  623.   }
  624.   var params = {};
  625.   params.user = aUsername;
  626.   this.call(myListener, "youtube.users.get_profile", params);
  627. }
  628.  
  629. youtubeService.prototype.getAlbums = function(aListener, aUsername) { throw "NotImplemented"; }
  630. youtubeService.prototype.getAuthPerson = function() {
  631.   if (!this.user) return null;
  632.   var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID]
  633.                              .createInstance(Components.interfaces.flockIPhotoPerson);
  634.   newUserObj.id = this.api.user.username
  635.   newUserObj.username = this.api.user.username;
  636.   newUserObj.fullname = this.api.user.username;
  637.   newUserObj.service = this;
  638.   return newUserObj;
  639. }
  640. youtubeService.prototype.getContacts = function(aListener) { throw "NotImplemented"; }
  641. youtubeService.prototype.getMostRecentPhotoForList = function(aListener, aEnumerator) { throw "NotImplemented"; }
  642. youtubeService.prototype.getPhoto = function(aListener, aPhotoID) { throw "NotImplemented"; }
  643. youtubeService.prototype.getValidPerson = function(aListener, aURL) { throw "NotImplemented"; }
  644. youtubeService.prototype.login = function(aAccountURN, aListener) { throw "NotImplemented"; }
  645.  
  646. youtubeService.prototype.queryChannel =
  647. function youtubeService_queryChannel(aListener, aQueryString, aCount, aPage)
  648. {
  649.   var aQuery = new queryHelper(aQueryString);
  650.   // return; // XXX TODO FIXME: This is killing perf
  651.  
  652.   var inst = this;
  653.   var myListener = {
  654.     onResult: function (aXML) {
  655.       var rval = inst.handlePhotosResult(aXML);
  656.       var enum_ = {
  657.         hasMoreElements: function() {
  658.           return (rval.length > 0);
  659.         },
  660.         getNext: function() {
  661.           return rval.shift();
  662.         }
  663.       }
  664.       aListener.onSearchResult(enum_);
  665.     },
  666.     onError: function (aError) {
  667.       aListener.onError(aError);
  668.     }
  669.   }
  670.  
  671.   var params = {}
  672.   if (aQuery.search) {
  673.     params.tag = aQuery.search;
  674.     params.page = aPage;
  675.     params.per_page = aCount;
  676.     this.call(myListener, 'youtube.videos.list_by_tag', params);
  677.   } else {
  678.     // this API call doesn't support pagination and only shows most recent 25 items
  679.     if (aPage > 1) return;
  680.  
  681.     var channel = channels[aQuery.stringVal];
  682.     if (!channel) return;
  683.  
  684.     if (channel.feed)
  685.       this.getFeed(aListener, channel.feed);
  686.     else
  687.       this.call(myListener, "youtube.videos.list_featured", params);
  688.   }
  689. }
  690.  
  691. youtubeService.prototype.search =
  692. function youtubeService_search( aListener, aQueryString, aCount, aPage )
  693. {
  694.  
  695.   var aQuery = new queryHelper(aQueryString);
  696.   if (aPage > 1) return; // youtube doesn't support pagination
  697.   if(aQuery.favorites && !aQuery.user)
  698.   {
  699.     aQuery.user = aQuery.favorites;
  700.   }
  701.   if (!aQuery.user && aQuery.special != "favorites") {
  702.        this.queryChannel( aListener, aQueryString, aCount, aPage );
  703.        return;
  704.   }
  705.  
  706.  
  707.  
  708.   var aUserid = aQuery.user;
  709.  
  710.   var params = {
  711.     user: aUserid
  712.   };
  713.  
  714.   var inst = this;
  715.   var myListener = {
  716.     onResult: function (aXML) {
  717.       var rval = inst.handlePhotosResult(aXML, aUserid);
  718.       var enum_ = {
  719.         hasMoreElements: function() {
  720.           return (rval.length > 0);
  721.         },
  722.         getNext: function() {
  723.           return rval.shift();
  724.         }
  725.       }
  726.       aListener.onSearchResult(enum_);
  727.     },
  728.     onError: function (aError) {
  729.       aListener.onError(aError);
  730.     }
  731.   }
  732.  
  733.   if (aQuery.favorites) {
  734.     this.call(myListener, "youtube.users.list_favorite_videos", params);
  735.   } else {
  736.     this.call(myListener, "youtube.videos.list_by_user", params);
  737.   }
  738. }
  739.  
  740. youtubeService.prototype.getPhotoFromRDFNode =
  741. function (aRDFId)
  742. {
  743.   var newPhoto = new youtubeVideo();
  744.   var coopPhoto = this.faves_coop.get(aRDFId);
  745.   newPhoto.webPageUrl = coopPhoto.URL;
  746.   newPhoto.thumbnail = coopPhoto.thumbnail;
  747.   newPhoto.midSizePhoto = coopPhoto.midSizePhoto;
  748.   newPhoto.largeSizePhoto = coopPhoto.largeSizePhoto;
  749.   newPhoto.username = coopPhoto.username;
  750.   newPhoto.userid = coopPhoto.userid;
  751.   newPhoto.title = coopPhoto.name;
  752.   newPhoto.id = coopPhoto.photoid;
  753.   newPhoto.icon = coopPhoto.favicon;
  754.   newPhoto.uploadDate = coopPhoto.datevalue;
  755.   newPhoto.is_public = coopPhoto.is_public;
  756.   newPhoto.is_video = "true";
  757.   return newPhoto;
  758.   // JMC XXX TODO: Gotta add the video-specific fields into the RDF, or somewhere
  759.   // eg content length, rating, etc.
  760.  
  761. }
  762.  
  763. youtubeService.prototype.handlePhotosResult =
  764. function youtubeService_handlePhotoResult(aXML, aUserid)
  765. {
  766.   var rval = [];
  767.   var photoList = aXML.getElementsByTagName("video");
  768.   for (var i = 0; i < photoList.length; i++) {
  769.     var photo = photoList[i];
  770.     var newPhoto = new youtubeVideo();
  771.     for (var j = 0; j < photo.childNodes.length; j++)
  772.     {
  773.       var child = photo.childNodes[j];
  774.       var contentNode = child.childNodes[0];
  775.       var childContent = "";
  776.       if (contentNode) childContent = contentNode.nodeValue;
  777.       switch (child.tagName)
  778.       {
  779.         case "author":
  780.           newPhoto.username   = childContent;
  781.           newPhoto.userid   = childContent;
  782.         break;
  783.         // case "id":     newPhoto.id     = childContent; break;
  784.         case "title":   newPhoto.title     = childContent; break;
  785.         case "upload_time":
  786.           newPhoto.uploadDate = childContent*1000;
  787.           newPhoto.id = parseInt(childContent);
  788.         break;
  789.         case "length_seconds":
  790.           newPhoto.length_seconds = childContent;
  791.           break
  792.         case "rating_avg":
  793.           newPhoto.rating_avg = childContent;
  794.           break
  795.         case "description":
  796.           newPhoto.description = childContent;
  797.           break
  798.         case "comment_count":
  799.           newPhoto.comment_count = childContent;
  800.           break
  801.         case "view_count":
  802.           newPhoto.view_count = childContent;
  803.           break
  804.         case "rating_count":
  805.           newPhoto.rating_count = childContent;
  806.           break
  807.         case "url":
  808.           newPhoto.webPageUrl  = childContent;
  809.         break;
  810.         case "thumbnail_url":
  811.           newPhoto.thumbnail     = childContent;
  812.           newPhoto.midSizePhoto   = childContent;
  813.           newPhoto.largeSizePhoto = childContent;
  814.         break;
  815.         // case "id":     newPhoto.id = childContent;
  816.         // break;
  817.       }
  818.     }
  819.  
  820.     newPhoto.is_public = "true";
  821.     newPhoto.is_video = true;
  822.     rval.push(newPhoto);
  823.   }
  824.   return rval;
  825. }
  826.  
  827. youtubeService.prototype.handlePeopleResult =
  828. function youtubeService_handlePeopleResult(aXML, aUserid)
  829. {
  830.   var rval = [];
  831.   var friendList = aXML.getElementsByTagName("friend");
  832.   for (var i = 0; i < friendList.length; i++) {
  833.     var person = friendList[i];
  834.     var newPerson = {};
  835.     for (var j = 0; j < person.childNodes.length; j++)
  836.     {
  837.       var child = person.childNodes[j];
  838.       var contentNode = child.childNodes[0];
  839.       var childContent = "";
  840.       if (contentNode) childContent = contentNode.nodeValue;
  841.       switch (child.tagName)
  842.       {
  843.         case "user":
  844.           newPerson.name   = childContent;
  845.           newPerson.accountId   = childContent;
  846.         break;
  847.         /*
  848.         case "friend_count":   newPerson.friend_count     = childContent; break;
  849.         case "video_upload_count":
  850.           newPerson.videoCount = childContent;
  851.         break;
  852.         */
  853.       }
  854.     }
  855.     rval.push(newPerson);
  856.   }
  857.   return rval;
  858. }
  859.  
  860.  
  861.  
  862. youtubeService.prototype.handleFeedResult =
  863. function youtubeService_handleFeedResult(aListener, aFeed)
  864. {
  865.   var photos = [];
  866.  
  867.   var items = aFeed.getItems();
  868.   while (items && items.hasMoreElements()) {
  869.     var item = items.getNext();
  870.  
  871.     var newPhoto = new youtubeVideo();
  872.  
  873.     newPhoto.title = item.getTitle();
  874.     newPhoto.webPageUrl = item.getLink().spec;
  875.  
  876.     newPhoto.uploadDate = item.getPubDate();
  877.     newPhoto.id = newPhoto.uploadDate / 1000;
  878.  
  879.     var author = item.getAuthor();
  880.     newPhoto.username = author;
  881.     newPhoto.userid = author;
  882.  
  883.     var desc = item.getContent();
  884.     var re = /<img [^>]*src="([^"]*)"/i;
  885.     var thumbnail = desc.match(re)[1];
  886.     newPhoto.thumbnail = thumbnail;
  887.     newPhoto.midSizePhoto = thumbnail;
  888.     newPhoto.largeSizePhoto = thumbnail;
  889.  
  890.     newPhoto.is_public = "true";
  891.     newPhoto.is_video = true;
  892.  
  893.     photos.push(newPhoto);
  894.   }
  895.  
  896.   var enum_ = {
  897.     hasMoreElements: function() {
  898.       return (photos.length > 0);
  899.     },
  900.     getNext: function() {
  901.       return photos.shift();
  902.     }
  903.   }
  904.   aListener.onSearchResult(enum_);
  905. }
  906.  
  907.  
  908. youtubeService.prototype.supportsFeature = function(aFeature) {
  909.   var supports = {};
  910.   supports.tags = true;
  911.   supports.title = true;
  912.   supports.fileName = false;
  913.   supports.contacts = true;
  914.   supports.privacy = false;
  915.   supports.albumCreation = false;
  916.   return (supports[aFeature] == true);
  917. }
  918. youtubeService.prototype.upload = function(aListener, aFile, aParams, aUpload) {  throw "NotImplemented"; }
  919. youtubeService.prototype.upload2 = function(aListener, aUpload, aFilename) {  throw "NotImplemented"; }
  920.  
  921.  
  922.  
  923. function dictionary2Params(aDictionary) {
  924.   var params = {};
  925.   var obj = {};
  926.   var count = {};
  927.   var keys = aDictionary.getKeys(count, obj);
  928.   for (var i = 0; i < keys.length; ++i) {
  929.     var supports = aDictionary.getValue(keys[i]);
  930.     var supportsString = supports.QueryInterface(Components.interfaces.nsISupportsString);
  931.     var val = supportsString.toString();
  932.     params[keys[i]] = val;
  933.   }
  934.   return params;
  935. }
  936.  
  937.  
  938. youtubeService.prototype.refresh =
  939. function youtubeService_refresh(aURN, aListener)
  940. {
  941.   DEBUG("refresh with aURN of " + aURN);
  942.   var refreshItem = this.faves_coop.get(aURN);
  943.  
  944.   if (refreshItem.isInstanceOf(this.faves_coop.Account)) {
  945.     this.refreshAccount(aURN, aListener);
  946.   } else if (refreshItem.isInstanceOf(this.faves_coop.Favorite)) {
  947.     this.refreshItem(aURN, aListener);
  948.   } else if (refreshItem.isInstanceOf(this.faves_coop.MediaQuery)) {
  949.     // TODO refresh MediaQueries?
  950.   } else {
  951.     throw Components.results.NS_ERROR_ABORT;
  952.   }
  953. }
  954.  
  955. youtubeService.prototype.refreshItem =
  956. function youtubeService_refreshItem(aURN, aListener)
  957. {
  958.   throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  959. }
  960.  
  961. youtubeService.prototype.refreshAccount =
  962. function youtubeService_refreshAccount(aURN, aListener)
  963. {
  964.   if (!aURN) return;
  965.   var refreshItem = this.faves_coop.get(aURN);
  966.   this.getFriends(aListener, refreshItem);
  967. }
  968.  
  969. // JMC - Convenience wrapper, probably overkill
  970. youtubeService.prototype.addFoafPerson =
  971. function youtubeService_addFoafPerson(aPhotoPerson, aFriendOfURN)
  972. {
  973. }
  974.  
  975. // JMC - XXX TODO - Need to sanity the diff between currAcct and aAccount in all
  976. // these methods, make sure it makes sense
  977.  
  978. youtubeService.prototype.addCoopPerson =
  979. function youtubeService_addCoopPerson(aPhotoPerson, bIsTransient, aFriendOfURN)
  980. {
  981. }
  982.  
  983. youtubeService.prototype.addMediaStream =
  984. function youtubeService_addMediaStream(aFriend, aCoopAccount)
  985. {
  986.   var query = new queryHelper();
  987.   query.user = aFriend.accountId;
  988.   query.username = aFriend.accountId;
  989.   var mediaqueryURN = "urn:media:favorites:youtube:"+query.stringVal;
  990.   var mediaquery = this.faves_coop.get(mediaqueryURN);
  991.   if (!mediaquery) {
  992.     mediaquery = new this.faves_coop.MediaQuery(
  993.       mediaqueryURN,
  994.       {
  995.         serviceId: PHOTOAPIMGR_CONTRACTID,
  996.         service: this.shortName,
  997.         favicon: YOUTUBE_FAVICON
  998.       }
  999.     );
  1000.   }
  1001.   mediaquery.query = query.stringVal;
  1002.   mediaquery.name = aFriend.name;
  1003.   mediaquery.isPollable = true;
  1004.   mediaquery.isTransient = aCoopAccount.isTransient;
  1005.   var mediaFavesURN = "urn:media:favorites";
  1006.   var mediaFaves = this.faves_coop.get(mediaFavesURN);
  1007.   if (!mediaFaves) {
  1008.     mediaFaves = new this.faves_coop.Folder(mediaFavesURN);
  1009.     this.faves_coop.favorites_root.children.add(mediaFaves);
  1010.   }
  1011.   mediaFaves.children.addOnce(mediaquery);
  1012. }
  1013.  
  1014. youtubeService.prototype.handleFriendsResult =
  1015. function youtubeService_handleFriendsResult(aXML, aAccount)
  1016. {
  1017.   DEBUG(".handleFriendsResult(aXML, aAccount)");
  1018.   var friendList = aXML.getElementsByTagName("friend");
  1019.   DEBUG(" - found "+friendList.length+" friends");
  1020.   for (var i = 0; i < friendList.length; i++) {
  1021.     var friend = friendList[i];
  1022.     var friendObj = {};
  1023.     for (var j = 0; j < friend.childNodes.length; j++)
  1024.     {
  1025.       var child = friend.childNodes[j];
  1026.       var contentNode = child.childNodes[0];
  1027.       var childContent = "";
  1028.       if (contentNode) childContent = contentNode.nodeValue;
  1029.       switch (child.tagName)
  1030.       {
  1031.         case "user":
  1032.         {
  1033.           friendObj.accountId = childContent;
  1034.           friendObj.name = childContent;
  1035.         }; break;
  1036.       }
  1037.     }
  1038.     //this.addCoopPerson(friendObj);
  1039.     this.addMediaStream(friendObj, aAccount);
  1040.   }
  1041. }
  1042.  
  1043.  
  1044. youtubeService.prototype.getFriends =
  1045. function youtubeService_getFriends(aListener, aAccount)
  1046. {
  1047.   var inst = this;
  1048.   var myListener = {
  1049.     onResult: function (aXML) {
  1050.       inst.handleFriendsResult(aXML, aAccount);
  1051.       aListener.onResult();
  1052.     },
  1053.     onError: function (aXML) {
  1054.       aListener.onError();
  1055.     }
  1056.   }
  1057.   var params = {};
  1058.   params.user = aAccount.accountId;
  1059.   this.call(myListener, "youtube.users.list_friends", params);
  1060. }
  1061.  
  1062. var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  1063.                        .getService(Components.interfaces.mozIJSSubScriptLoader);
  1064. loader.loadSubScript("chrome://browser/content/utilityOverlay.js");
  1065.  
  1066.  
  1067. youtubeService.prototype.doAction =
  1068. function youtubeService_doAction(aMethodName, aURN, aSubject)
  1069. {
  1070.  
  1071.   var actionTarget = this.faves_coop.get(aURN);
  1072.   switch (aMethodName)
  1073.   {
  1074.     case "openProfile":
  1075.       return gStrings["userprofile"].replace("%accountid%", actionTarget.accountId);
  1076.       break;
  1077.     case "openSubscriptions":
  1078.       return gStrings["subscriptions"];
  1079.       break;
  1080.     case "openVideos":
  1081.       // return "http://youtube.com/my_videos";
  1082.       var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  1083.                               .getService(Components.interfaces.nsIWindowMediator);
  1084.       var win = wm.getMostRecentWindow('navigator:browser');
  1085.       if (win) {
  1086.         win.gPhotoDrawer.loadPhotoPerson(aURN, this.shortName);
  1087.       }
  1088.       break;
  1089.     case "openFavVideos":
  1090.       // return "http://youtube.com/my_favorites";
  1091.       var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  1092.                          .getService(Components.interfaces.nsIWindowMediator);
  1093.       var win = wm.getMostRecentWindow('navigator:browser');
  1094.       if (win) {
  1095.         win.gPhotoDrawer.loadQuery(this.shortName, "favorites:" + actionTarget.accountId, actionTarget.name);
  1096.       }
  1097.       break;
  1098.     case "openInbox":
  1099.       return gStrings["inbox"];
  1100.       break;
  1101.     case "editProfile":
  1102.       return gStrings["editprofile"];
  1103.       break;
  1104.     case "postComment":
  1105.       return actionTarget.URL + "?mode=reply";
  1106.       break;
  1107.     case "viewFriends":
  1108.     // Dangerous assumption that the people sidebar is open when this is called
  1109.       var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  1110.                          .getService(Components.interfaces.nsIWindowMediator);
  1111.       var win = wm.getMostRecentWindow('navigator:browser');
  1112.       if (win) {
  1113.         var peepsSidebar = win.document.getElementById('sidebar').contentWindow;
  1114.         if (peepsSidebar)
  1115.           peepsSidebar.gPeopleBrowser.getCurrent(actionTarget.id());
  1116.       }
  1117.       break;
  1118.     default:
  1119.       dump("BORK BORK - undefined action in youtube\n");
  1120.       break;
  1121.   }
  1122.   return null;
  1123. }
  1124.  
  1125. youtubeService.prototype.updateActions =
  1126. function youtubeService_updateActions(aURN)
  1127. {
  1128.   var coopObj = this.faves_coop.get(aURN);
  1129.   if (coopObj.isInstanceOf(this.faves_coop.Account)) {
  1130.     coopObj.enabledAction.removeAll();
  1131.     var serviceActions = this.ytService.children.enumerate();
  1132.     while (serviceActions.hasMoreElements()) {
  1133.       var action = serviceActions.getNext();
  1134.       if (action.flavour == "accountaction" ||
  1135.           action.flavour == "accountview" ||
  1136.           action.flavour == "view")
  1137.       {
  1138.         coopObj.enabledAction.add(action);
  1139.       }
  1140.     }
  1141.   }
  1142. }
  1143.  
  1144.  
  1145. // BEGIN flockIWebService inteface
  1146. youtubeService.prototype.addAccountById =
  1147. function youtubeService_addAccountById(aAccountID, aIsTransient, aListener)
  1148. {
  1149.   DEBUG("{flockIWebService}.addAccountById('"+aAccountID+"', "+aIsTransient+", aListener)");
  1150.  
  1151.   var accountURN = "urn:flock:youtube:"+aAccountID;
  1152.   var account = new this.faves_coop.Account(
  1153.     accountURN,
  1154.     {
  1155.       name: aAccountID,
  1156.       serviceId: YOUTUBE_CONTRACTID,
  1157.       service: this.ytService,
  1158.       accountId: aAccountID,
  1159.       URL: gStrings["userprofile"].replace("%accountid%", aAccountID),
  1160.       isTransient: aIsTransient,
  1161.       favicon: YOUTUBE_FAVICON
  1162.     }
  1163.   );
  1164.   this.account_root.children.add(account);
  1165.   this.USER = account.id();
  1166.   var notifUrn = accountURN + ":notifications";
  1167.   var notifications = new this.faves_coop.Stream(
  1168.     notifUrn,
  1169.     {
  1170.       name: "YouTube Notifications",
  1171.       isPollable: false,
  1172.       isIndexable: false,
  1173.       notify: true,
  1174.       serviceId: YOUTUBE_CONTRACTID
  1175.     }
  1176.   );
  1177.   account.children.addOnce(notifications);
  1178.  
  1179.   // Instanciate account component
  1180.   var acct = this.getAccount(account.id());
  1181.   if (aListener) aListener.onSuccess(acct, "addAccount");
  1182.   return acct;
  1183. }
  1184. // END flockIWebService interface
  1185.  
  1186.  
  1187. // BEGIN flockIManageableWebService interface
  1188. youtubeService.prototype.updateAccountStatusFromDocument =
  1189. function youtubeService_updateAccountStatusFromDocument(aDocument)
  1190. {
  1191.   DEBUG("{flockIManageableWebService}.updateAccountStatusFromDocument(aDocument)");
  1192.   if (this.docRepresentsSuccessfulLogin(aDocument)) {
  1193.     var accountID = this.getAccountIDFromDocument(aDocument);
  1194.     var acctURN = this.acUtils.getAccountURNById(this.urn, accountID);
  1195.     var accounts = this.faves_coop.Account.find({serviceId: YOUTUBE_CONTRACTID});
  1196.     for (var i = 0; i < accounts.length; i++) {
  1197.       if (accounts[i].id() == acctURN) {
  1198.         accounts[i].isAuthenticated = true;
  1199.         this.USER = acctURN;
  1200.       } else {
  1201.         accounts[i].isAuthenticated = false;
  1202.       }
  1203.     }
  1204.   } else {
  1205.     this.acUtils.markAllAccountsAsLoggedOut(YOUTUBE_CONTRACTID);
  1206.   }
  1207. }
  1208. // END flockIManageableWebService interface
  1209.  
  1210.  
  1211. // BEGIN flockISocialWebService interface
  1212. youtubeService.prototype.decorateForPerson =
  1213. function youtubeService_decorateForPerson(aDocument)
  1214. {
  1215.   DEBUG("{flockISocialWebService}.decorateForPerson()");
  1216. }
  1217.  
  1218. youtubeService.prototype.browseFriends =
  1219. function youtubeService_browseFriends(aFriendURN, aListener)
  1220. {
  1221.   DEBUG("{flockISocialWebService}.browseFriends('"+aFriendURN+"')");
  1222. }
  1223. // END flockISocialWebService interface
  1224.  
  1225.  
  1226. youtubeService.prototype.getFriendsContacts =
  1227. function youtubeService_getFriendsContacts(aListener, aUsername)
  1228. {
  1229.   var inst = this;
  1230.   var myListener = {
  1231.     onResult: function (aXML) {
  1232.       var rval = inst.handlePeopleResult(aXML);
  1233.       var enum_ = {
  1234.         hasMoreElements: function() {
  1235.           return (rval.length > 0);
  1236.         },
  1237.         getNext: function() {
  1238.           return rval.shift();
  1239.         }
  1240.       }
  1241.       aListener.onGetContactsResult(enum_);
  1242.     },
  1243.     onError: function (aXML) {
  1244.       aListener.onError(aXML);
  1245.     }
  1246.   }
  1247.   var params = {};
  1248.   params.user = aUsername;
  1249.   this.call(myListener, "youtube.users.list_friends", params);
  1250. }
  1251.  
  1252.  
  1253. // BEGIN flockIMediaWebService interface
  1254. youtubeService.prototype.decorateForMedia =
  1255. function youtubeService_decorateForMedia(aDocument)
  1256. {
  1257.   DEBUG("{flockIMediaWebService}.decorateForMedia(aDocument)");
  1258.   aDocument.QueryInterface(Components.interfaces.nsIDOMHTMLDocument);
  1259.   var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1260.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1261.   if (this.webDetective.detect("youtube", "media", aDocument, results)) {
  1262.     var mediaArr = [];
  1263.  
  1264.     // media item for user videos
  1265.     var userid = results.getPropertyAsAString("userid");
  1266.     var media = {
  1267.       name: userid,
  1268.       query: 'user:' + userid + "|username:" + userid,
  1269.       label: userid + "'s Videos", // FIXME: breaks internationalization
  1270.       favicon: this.icon,
  1271.       service: this.shortName
  1272.     }
  1273.     mediaArr.push(media);
  1274.  
  1275.     // media item for user favorites
  1276.     var media = {
  1277.       name: userid,
  1278.       query: 'favorites:' + userid,
  1279.       label: userid + "'s Favorites", // FIXME: breaks internationalization
  1280.       favicon: this.icon,
  1281.       service: this.shortName
  1282.     }
  1283.     mediaArr.push(media);
  1284.  
  1285.     if (!aDocument._flock_decorations) {
  1286.       aDocument._flock_decorations = {};
  1287.     }
  1288.     aDocument._flock_decorations.mediaArr = mediaArr;
  1289.     this.obs.notifyObservers(aDocument, 'media', 'media:update');
  1290.   }
  1291. }
  1292.  
  1293. youtubeService.prototype.handlesMediaStream =
  1294. function youtubeService_handlesMediaStream() 
  1295. {
  1296.   return true;
  1297. }
  1298.  
  1299. youtubeService.prototype.checkIsStreamUrl =
  1300. function youtubeService_checkIsStreamUrl(aUrl)
  1301. {
  1302.   this._logger.debug("Checking Stream Url for Youtube: " + aUrl);
  1303.   if (aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/v\/.*/) ||
  1304.       aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\/v\/.*/) ||
  1305.       aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\?v=.*/) ||
  1306.       aUrl.match(/^\/player\w*\.swf\?v*/) ) {
  1307.     return true;
  1308.   }
  1309.   
  1310.   return false;
  1311. }
  1312.  
  1313. youtubeService.prototype.getVideoIDFromUrl = 
  1314. function youTubeService_getVideoIDFromUrl(aUrl)
  1315. {
  1316.   // These are image link matches
  1317.   //http://www.youtube.com/v/lp_daXRkOH0
  1318.   userMatch = aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/v\/(.*)/);
  1319.   if (userMatch) {
  1320.     return userMatch[1];
  1321.   }
  1322.   //http://www.youtube.com/watch/v/YP2rgi978tw
  1323.   userMatch = aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\/v\/(.*)/);
  1324.   if (userMatch) {
  1325.     return userMatch[1];
  1326.   }
  1327.   //http://www.youtube.com/watch?v=BjfbS_Kj-J0
  1328.   userMatch = aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\?v=(.*)/);
  1329.   if (userMatch) {
  1330.     return userMatch[1];
  1331.   }
  1332.   
  1333.   // These are flash video sources matching
  1334.   // /player2.swf?video_id=xPxDw7ajfGE&....
  1335.   var userMatch = aUrl.match(/^\/player2\.swf\?.*video_id=([^&]+).*/);
  1336.   if (userMatch){
  1337.     return userMatch[1];
  1338.   }
  1339.   
  1340.   return null;
  1341. }
  1342.  
  1343. youtubeService.prototype.getMediaQueryFromURL =
  1344. function youTubeService_getMediaQueryFromURL(aUrl, aListener)
  1345. {
  1346.   var videoId = this.getVideoIDFromUrl(aUrl);
  1347.   if (videoId) {
  1348.     var myListener = {
  1349.       onResult: function (aXML) {
  1350.         var userID = aXML.getElementsByTagName('author')[0].firstChild.nodeValue;
  1351.         var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1352.                                 .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1353.         results.setPropertyAsAString("query", "user:" + userID + "|username:" + userID);
  1354.         results.setPropertyAsAString("title", this.title + " user: " + userID);
  1355.         aListener.onSuccess(results, "query");
  1356.       },
  1357.       onError: function (aError) {
  1358.        aListener.onError(null, aError, null);
  1359.       }
  1360.     }
  1361.     var params = {};
  1362.     params.video_id = videoId;
  1363.     this.call(myListener, "youtube.videos.get_details", params);
  1364.   } else {
  1365.     aListener.onError(null, "Unable to get user.", null);
  1366.   }
  1367. }
  1368.  
  1369. // END flockIMediaWebService interface
  1370.  
  1371.  
  1372. // ========== END youtubeService class ==========
  1373.  
  1374.  
  1375.  
  1376. // ================================================
  1377. // ========== BEGIN youtubeAccount class ==========
  1378. // ================================================
  1379.  
  1380. function youtubeAccount()
  1381. {
  1382.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  1383.                            .getService(Components.interfaces.flockIAccountUtils);
  1384.   this.service = Components.classes[YOUTUBE_CONTRACTID]
  1385.                            .getService(Components.interfaces.flockIWebService)
  1386.                            .QueryInterface(Components.interfaces.flockISocialWebService);
  1387.   this._coop = Components.classes["@flock.com/singleton;1"]
  1388.                          .getService(Components.interfaces.flockISingleton)
  1389.                          .getSingleton("chrome://browser/content/flock/common/load-faves-coop.js")
  1390.                          .wrappedJSObject;
  1391.   this._ctk = {
  1392.     interfaces: [
  1393.       "nsISupports",
  1394.       "flockIWebServiceAccount",
  1395.       "flockIMediaWebServiceAccount",
  1396.       "flockISocialWebServiceAccount",
  1397.     ]
  1398.   };
  1399.   getCompTK().addAllInterfaces(this);
  1400. }
  1401.  
  1402.  
  1403. // BEGIN flockIWebServiceAccount interface
  1404. youtubeAccount.prototype.urn = "";
  1405. youtubeAccount.prototype.username = "";
  1406. youtubeAccount.prototype.status = "";
  1407.  
  1408. youtubeAccount.prototype.activate =
  1409. function youtubeAccount_activate(aListener)
  1410. {
  1411.   DEBUG("{flockIWebServiceAccount}.activate()");
  1412.   var acctCoopObj = this._coop.get(this.urn);
  1413.   acctCoopObj.isPollable = true;
  1414.   if (aListener) {
  1415.     aListener.onSuccess(this, "accountAuthorized");
  1416.   }
  1417. }
  1418.  
  1419. youtubeAccount.prototype.login =
  1420. function youtubeAccount_login(aListener)
  1421. {
  1422.   DEBUG("{flockIWebServiceAccount}.login()");
  1423. }
  1424. // END flockIWebServiceAccount interface
  1425.  
  1426.  
  1427. // BEGIN flockISocialWebServiceAccount interface
  1428. youtubeAccount.prototype.browseFriends =
  1429. function youtubeAccount_browseFriends(aFriendURN, aListener)
  1430. {
  1431.   DEBUG("{flockISocialWebServiceAccount}.browseFriends('"+aFriendURN+"')");
  1432.   this.service.browseFriends(aFriendURN, aListener);
  1433. }
  1434. // END flockISocialWebServiceAccount interface
  1435.  
  1436. // ========== END youtubeAccount class ==========
  1437.  
  1438.  
  1439.  
  1440. // ==============================================
  1441. // ========== BEGIN XPCOM registration ==========
  1442. // ==============================================
  1443.  
  1444. function createModule(aParams) {
  1445.   return {
  1446.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  1447.       aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  1448.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  1449.                                         aParams.contractID, aFileSpec,
  1450.                                         aLocation, aType );
  1451.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  1452.         .getService(Ci.nsICategoryManager);
  1453.       if (!aParams.categories) { aParams.categories = []; }
  1454.       for (var i = 0; i < aParams.categories.length; i++) {
  1455.         var cat = aParams.categories[i];
  1456.         catMgr.addCategoryEntry( cat.category, cat.entry,
  1457.                                  cat.value, true, true );
  1458.       }
  1459.     },
  1460.     getClassObject: function (aCompMgr, aCID, aIID) {
  1461.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  1462.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  1463.       return { // Factory
  1464.         createInstance: function (aOuter, aIID) {
  1465.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  1466.           var comp = new aParams.componentClass();
  1467.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  1468.           return comp.QueryInterface(aIID);
  1469.         }
  1470.       };
  1471.     },
  1472.     canUnload: function (aCompMgr) { return true; }
  1473.   };
  1474. }
  1475.  
  1476. // NS Module entrypoint
  1477. function NSGetModule(aCompMgr, aFileSpec) {
  1478.   return createModule({
  1479.     componentClass: youtubeService,
  1480.     CID: YOUTUBE_CID,
  1481.     contractID: YOUTUBE_CONTRACTID,
  1482.     componentName: CATEGORY_COMPONENT_NAME,
  1483.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
  1484.     categories: [
  1485.       { category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: YOUTUBE_CONTRACTID },
  1486.       { category: "flockIPhotoAPI", entry: CATEGORY_ENTRY_NAME, value: YOUTUBE_CONTRACTID },
  1487.       { category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: YOUTUBE_CONTRACTID }
  1488.     ]
  1489.   });
  1490. }
  1491.  
  1492. // ========== END XPCOM registration ==========
  1493.  
  1494.  
  1495.  
  1496. // HELPER STUFF
  1497.  
  1498. function loadSubScript(spec)
  1499. {
  1500.   var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  1501.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  1502.   var context = {};
  1503.   loader.loadSubScript(spec, context);
  1504.   return context;
  1505. }
  1506.  
  1507. function loadLibraryFromSpec(aSpec)
  1508. {
  1509.   var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  1510.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  1511.   loader.loadSubScript(aSpec);
  1512. }
  1513.